﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using VSAgent.VSServiceReference;
using EnvDTE80;
using System.ServiceModel;
using System.IO;
using Process = System.Diagnostics.Process;
using Debugger = System.Diagnostics.Debugger;
using System.Threading;
using AbstraX.Contracts;
using System.Xml;

namespace VSAgent
{
    public class VSInstanceClient
    {
        private VSServiceClient vsServiceClient;
        private Guid userID;
        private DTE2 applicationObject;
        private AddIn addInInstance;
        private Timer timer;
        private bool isRegistered;
        private SolutionEvents solutionEvents;

        public VSInstanceClient(DTE2 applicationObject, AddIn addInInstance)
        {
            var binding = new WSHttpBinding();
            var readerQuotas = new XmlDictionaryReaderQuotas();

            readerQuotas.MaxStringContentLength = Int32.MaxValue;
            readerQuotas.MaxArrayLength = Int32.MaxValue;
            readerQuotas.MaxBytesPerRead = Int32.MaxValue;
            readerQuotas.MaxDepth = Int32.MaxValue;
            readerQuotas.MaxNameTableCharCount = Int32.MaxValue;

            solutionEvents = applicationObject.Events.SolutionEvents;

            solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SolutionEventsOpened);
            
            binding.SendTimeout = TimeSpan.FromMinutes(1);
            binding.OpenTimeout = TimeSpan.FromMinutes(1);
            binding.CloseTimeout = TimeSpan.FromMinutes(1);
            binding.ReceiveTimeout = TimeSpan.FromMinutes(10);
            binding.AllowCookies = false;
            binding.BypassProxyOnLocal = false;
            binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
            binding.MessageEncoding = WSMessageEncoding.Text;
            binding.TextEncoding = System.Text.Encoding.UTF8;
            binding.UseDefaultWebProxy = true;
            binding.MaxBufferPoolSize = int.MaxValue;
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReaderQuotas = readerQuotas;

            this.applicationObject = applicationObject;
            this.addInInstance = addInInstance;

            vsServiceClient = new VSServiceClient(binding, new EndpointAddress("http://localhost:49586/VSService.svc"));
        }

        private void SolutionEventsOpened()
        {
            if (!isRegistered)
            {
                var solution = (_Solution)applicationObject.Solution;
                var fileName = solution.FullName;
                var name = Path.GetFileNameWithoutExtension(fileName);

                if (Guid.TryParse(name, out userID))
                {
                    var process = Process.GetCurrentProcess();
                    var processID = process.Id;

                    solution.Close();
                    File.Delete(fileName);

                    vsServiceClient.RegisterInstance(userID, processID);

                    timer = new Timer(TimerCallback, null, 100, 100);

                    isRegistered = true;
                }
            }
        }

        private void TimerCallback(object state)
        {
            if (Monitor.TryEnter(this))
            {
                try
                {
                    var message = vsServiceClient.GetMessage(userID);

                    if (message != null)
                    {
                        switch (message.Message)
                        {
                            case Messages.OpenSolution:

                                applicationObject.Solution.Open(message.Params);
                                vsServiceClient.ReturnOpenVSSolution(userID, new VSSolution() { Name = applicationObject.Solution.FullName });

                                break;

                            case Messages.GetProjects:

                                var projects = new List<VSProject>();

                                foreach (Project project in applicationObject.Solution.Projects)
                                {
                                    projects.Add(new VSProject() { Name = project.Name });
                                }

                                vsServiceClient.ReturnGetProjects(userID, projects.ToArray());

                                break;

                            case Messages.GetModels:

                                if (applicationObject.Solution.Projects.Cast<Project>().Any(p => p.Name == message.Params))
                                {
                                    var project = applicationObject.Solution.Projects.Cast<Project>().Single(p => p.Name == message.Params);
                                    var models = new List<VSProjectItem>();

                                    foreach (ProjectItem item in project.ProjectItems)
                                    {
                                        if (item.Name.EndsWith(".edmx"))
                                        {
                                            models.Add(new VSProjectItem() { Name = item.Name, FileNames = item.GetFileNames() });
                                        }
                                    }

                                    vsServiceClient.ReturnGetModels(userID, models.ToArray());
                                }

                                break;

                            case Messages.GetFileContents:

                                var fileName = message.Params;
                                var bytes = File.ReadAllBytes(fileName);

                                vsServiceClient.ReturnGetFileContents(userID, bytes);

                                break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Monitor.Exit(this);
                    Debugger.Break();
                }

                Monitor.Exit(this);
            }
        }

        public void UnRegister()
        {
            var processID = Process.GetCurrentProcess().Id;

            timer.Dispose();
            vsServiceClient.UnRegisterInstance(userID, processID);
        }
    }
}
